﻿Imports Microsoft.WindowsMobile.PocketOutlook
Imports System.Runtime.InteropServices
Imports System.Xml
Imports System.Security.Cryptography
Imports System.IO



Module Module1

    Public Class ThreadManager
        Private m_Thread As System.Threading.Thread
        Private m_AppSettingsCollection As ImapPusher.AppSettingsCollection
        Private m_MailboxName As String
        Private m_ConnMgrReady As Boolean
        Private WithEvents m_Client As ImapPusher.ImapClient

        Public Property Thread() As System.Threading.Thread
            Get
                Return m_Thread
            End Get
            Set(ByVal value As System.Threading.Thread)
                m_Thread = value
            End Set
        End Property

        Public Property AppSettingsCollection() As ImapPusher.AppSettingsCollection
            Get
                Return m_AppSettingsCollection
            End Get
            Set(ByVal value As ImapPusher.AppSettingsCollection)
                m_AppSettingsCollection = value
            End Set
        End Property

        Public Property MailboxName() As String
            Get
                Return m_MailboxName
            End Get
            Set(ByVal value As String)
                m_MailboxName = value
            End Set
        End Property

        Public Property ConnMgrReady() As Boolean
            Get
                Return m_ConnMgrReady
            End Get
            Set(ByVal value As Boolean)
                m_ConnMgrReady = value
            End Set
        End Property

        Public Property Client() As ImapPusher.ImapClient
            Get
                Return m_client
            End Get
            Set(ByVal value As ImapPusher.ImapClient)
                m_client = value
            End Set
        End Property

        Private Sub m_client_NotifyMail() Handles m_Client.NotifyMail
            Trace.WriteLine("getting new messages")
            'mail has arrived, get outlook to send and receive please.
            ServerManager.SendHealthy(m_MailboxName)
            MailSyncMessages(MailboxName, 9)
        End Sub

        Public Sub New(ByVal pAppSettingsCollection As ImapPusher.AppSettingsCollection, ByVal pMailboxName As String, ByVal pConnMgrReady As Boolean)
            m_AppSettingsCollection = pAppSettingsCollection
            m_MailboxName = pMailboxName
            m_ConnMgrReady = pConnMgrReady
        End Sub

        Sub WorkerThread()
            Dim _threadManager As ThreadManager = Me
            Dim _as As ImapPusher.AppSettings = _threadManager.AppSettingsCollection.Settings(_threadManager.MailboxName)

            Dim _connectionUrl As String
            If Not _as.Server.Contains("http://") Then
                _connectionUrl = "http://" & _as.Server
            Else
                _connectionUrl = _as.Server
            End If

            If _threadManager.ConnMgrReady Then
                Dim _errBuff As New Text.StringBuilder(1024)
                If ImapPusher.NativeFuncs.ensureconnected(_errBuff, _connectionUrl, _as.UseSpecific, _as.SpecificConnection) = -1 Then
                    Trace.WriteLine("can't ensure connected: " & _errBuff.ToString)
                    Trace.WriteLine("continuing anyway.")
                End If
            End If

            Dim _pp As New ProvidePassword
            _pp.PasswordEnabled = String.IsNullOrEmpty(_as.Password)
            _pp.ShowDialog()

            If Not _pp.Agreed Then
                Trace.WriteLine("User did not agree, exiting.")
                Exit Sub
            End If

            If _pp.PasswordEnabled Then
                Trace.WriteLine("User provided as password...")
                _as.Password = _pp.txtPassword.Text
            End If

            'Create the client based on the settings.
            _threadManager.Client = New ImapPusher.ImapClient(_as.Server, _as.Port, _as.Refresh)
            Dim _lastRetry As Date = DateTime.Now
            Dim _retryTimer As TimeSpan = New TimeSpan(0, 0, 5)
            While True
                Try
                    _threadManager.Client.Connect(_as.Username, _as.Password)
                Catch ex As ImapPusher.CantContinueException
                    ServerManager.SendError(MailboxName, ex.Message)
                    Trace.WriteLine("can't continue, exiting thread worker.")
                    Exit Sub
                Catch ex As Exception
                    ServerManager.SendError(MailboxName, ex.Message)
                    Trace.WriteLine("socket errored out." & vbNewLine & ex.ToString)
                    If DateTime.Now.Subtract(_lastRetry) > New TimeSpan(0, 20, 0) Then
                        Trace.WriteLine("last retry was a while ago, resetting the timespan")
                        _retryTimer = New TimeSpan(0, 0, 5)
                    End If
                    System.Threading.Thread.Sleep(_retryTimer.TotalMilliseconds)
                    _retryTimer = TimeSpan.FromMilliseconds(_retryTimer.TotalMilliseconds * 2)
                    Trace.WriteLine("retry timer is now: " & _retryTimer.TotalMilliseconds)
                    _lastRetry = DateTime.Now
                    'reset the client to revert state to usable.
                    _threadManager.Client.Disconnect()
                    _threadManager.Client = New ImapPusher.ImapClient(_as.Server, _as.Port, _as.Refresh)
                End Try
            End While
        End Sub
    End Class

    Sub Main()
        Dim _asc As New ImapPusher.AppSettingsCollection

        If _asc.DoTracing Then
            Dim _listener As New TimeTextWriterTraceListener(_asc.TraceFileLocation)
            Trace.Listeners.Add(_listener)
            Trace.AutoFlush = True
        End If
        Trace.WriteLine("DSDS About to send PID for first time.")
        ServerManager.SendPID()

        Dim _connMgrReady As Boolean = ImapPusher.ConnectionsEnumerator.EnsureConnMgrReady

        Dim _lm As New ListenerManager(_asc.StatusPort)
        Dim _lthread As New System.Threading.Thread(New Threading.ThreadStart(AddressOf _lm.WorkerThread))
        _lthread.Start()

        For Each _appSetting As ImapPusher.AppSettings In _asc.Settings.Values
            Dim _tm As New ThreadManager(_asc, _appSetting.MailboxName, _connMgrReady)
            Dim _thread As New System.Threading.Thread(New Threading.ThreadStart(AddressOf _tm.WorkerThread))
            _thread.Start()
        Next
    End Sub

    Public Class ListenerManager
        Private m_Listener As Net.Sockets.TcpListener

        Public Sub New(ByVal pPort As Integer)
            m_Listener = New Net.Sockets.TcpListener(Net.IPAddress.Loopback, pPort)
        End Sub

        Public Sub WorkerThread()
            m_Listener.Start()
            While True
                Dim _Client As Net.Sockets.TcpClient = m_Listener.AcceptTcpClient()
                ServerManager.ProvideClient(_Client)
            End While
        End Sub
    End Class

    Public Class ServerManager
        Private Shared m_Lock As New Object
        Private Shared m_PID As Integer = -1
        Private Shared m_NS As Net.Sockets.NetworkStream
        Private Shared m_LastFailedOnServer As DateTime = DateTime.MinValue
        Private Shared m_Client As Net.Sockets.TcpClient

        Public Shared Sub ProvideClient(ByVal pClient As Net.Sockets.TcpClient)
            SyncLock m_Lock
                Trace.WriteLine("DSDS Client connected to me.")
                m_Client = pClient
                m_NS = Nothing
                Trace.WriteLine("DSDS Sending new client PID.")
                SendPID()
            End SyncLock
        End Sub

        Public Shared Sub SendString(ByVal pString As String)
            SyncLock m_Lock
                If m_Client IsNot Nothing Then
                    Try
                        If m_NS Is Nothing Then m_NS = m_Client.GetStream
                        Dim _bytes As Byte() = System.Text.Encoding.ASCII.GetBytes(pString & ControlChars.NewLine)
                        m_NS.Write(_bytes, 0, _bytes.Length)
                    Catch ex As Exception
                        Trace.WriteLine("DSDS Error writing back to status, there may be no client connected: " & ex.Message)
                        m_NS = Nothing
                        m_Client = Nothing
                    End Try
                End If
            End SyncLock
        End Sub

        Public Shared Sub SendPID()
            Dim _str As String
            SyncLock m_Lock
                If m_PID = -1 Then
                    m_PID = System.Diagnostics.Process.GetCurrentProcess().Id
                End If
                _str = "PID@S@" & m_PID.ToString
            End SyncLock
            SendString(_str)
        End Sub
        Public Shared Sub SendHealthy(ByVal pMailbox As String)
            Dim _str As String = "HEALTH@S@" & pMailbox
            SendPID()
            SendString(_str)
        End Sub
        Public Shared Sub SendLog(ByVal pInfo As String)
            Dim _str As String = "LOG@S@" & pInfo
            SendPID()
            SendString(_str)
        End Sub
        Public Shared Sub SendError(ByVal pMailbox As String, ByVal pError As String)
            Dim _str As String = "ERROR@S@" & pMailbox & "@S@" & pError
            SendPID()
            SendString(_str)
        End Sub
    End Class

    Public Class TimeTextWriterTraceListener
        Inherits TextWriterTraceListener

        Private m_LastSentPID As DateTime = DateTime.MinValue
        Private m_Lock As New Object

        Public Sub New(ByVal pParam As String)
            MyBase.New(pParam)
        End Sub

        Public Overrides Sub WriteLine(ByVal message As String)
            If Not message.StartsWith("DSDS ") Then
                ServerManager.SendLog(message)
            End If
            MyBase.WriteLine(Date.Now.ToShortTimeString & ":" & message)
        End Sub
    End Class

    <DllImport("cemapi.dll")> _
    Public Function MailSyncMessages(ByVal pAccountName As String, ByVal pFlags As UInt32) As Integer
    End Function

    <DllImport("cemapi.dll")> _
    Public Function MailSwitchToAccount(ByVal pAccountName As String, ByVal pFlags As UInt32) As Integer
    End Function
    
End Module




